﻿/* Copyright (c) 2010 Lexalytics Inc.

 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
 files (the "Software"), to deal in the Software without
 restriction, including without limitation the rights to use,
 copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the
 Software is furnished to do so, subject to the following
 conditions:

 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 OTHER DEALINGS IN THE SOFTWARE.
*/

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using System.Net;
using System.Xml;
using System.Web;

// Used for retrieving string resources
using System.Resources;
using System.Reflection;

namespace LexaScopeNET
{
    /// <summary>
    /// Class providing interface to web service methods as static methods.
    /// </summary>
    public class LXAWebService
    {

        #region Lexascope web service interface

        #region These functions are used to add specific items into Tempest
        /// <summary>
        /// Wrapper around POST to web service method to add a document to the project
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">Public key for web service request</param>
        /// <param name="PrivateKey">Private key for encoding web service request</param>
        /// <param name="ProjectKey">Project to add asset into</param>
        /// <param name="DataHash">Identifier for the content</param>
        /// <param name="DataContent">The content</param>
        /// <param name="EntityCount">Number of entities detected in asset</param>
        /// <param name="ThemeCount">Number of themes detected in the asset</param>
        /// <param name="SentimentScore">Sentiment score for the asset</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Status string</returns>
        public int WSAddAsset(string ServiceURL, string PublicKey, string PrivateKey, string ProjectKey, string DataHash, string DataContent)
        {
            int nReturnCode = -1;
            string strEncodedKey = "";

            try
            {
                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                myContent.Add("project", ProjectKey);
                myContent.Add("id", DataHash);
                myContent.Add("text", DataContent);
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                myContent.Add("encodedkey", strEncodedKey);

                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Perform upload up to certain number of failures
                int nTry = 1;
                // Construct web client object to make the request with params
                WebClient myClient = new WebClient();
                myClient.BaseAddress = ServiceURL;
                myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                // UploadValues performs implicit POST of NVC to BaseAddress + URI
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);

                XmlDocument addDocumentXml = new XmlDocument();
                while (nTry < 10)
                {
                    String responseXml = Encoding.ASCII.GetString(responseArray);
                    if (responseXml.StartsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))
                    {
                        addDocumentXml.LoadXml(Encoding.ASCII.GetString(responseArray));
                        LXAResponse addDocumentResponse = new LXAResponse(addDocumentXml);
                        nReturnCode = addDocumentResponse.LastError;
#if DEBUG
                        System.Diagnostics.Trace.WriteLine(String.Format("Try {0}: nReturn {1}", nTry, nReturnCode));
#endif
                        if (nReturnCode != 0)
                        {
#if DEBUG
                            System.Diagnostics.Trace.WriteLine("ERROR: " + addDocumentResponse.LastErrorString);
#endif
                            throw new Exception(addDocumentResponse.LastErrorString);
                        }
                        break;
                    }
                    else
                    {
#if DEBUG
                        System.Diagnostics.Trace.WriteLine(String.Format("Try {0}: Return {1}", nTry, addDocumentXml.InnerText));
#endif
                        nTry++;
                        responseArray = myClient.UploadValues(strMethodURL, myContent);
                    }
                }

            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to add the document.", xmlErr);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("XML ERR: " + xmlErr.Message);
#endif
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to add the document.", err);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("ERROR: " + err.Message);
#endif
                throw wsErr;
            }

            return nReturnCode;
        }

        /// <summary>
        /// Wrapper around POST to web service method to add a document to the project
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">Public key for web service request</param>
        /// <param name="PrivateKey">Private key for encoding web service request</param>
        /// <param name="DataHash">Identifier for the content</param>
        /// <param name="DataContent">The content</param>
        /// <param name="EntityCount">Number of entities detected in asset</param>
        /// <param name="ThemeCount">Number of themes detected in the asset</param>
        /// <param name="SentimentScore">Sentiment score for the asset</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Status string</returns>
        public int WSAddAssetWithResults(string ServiceURL, string PublicKey, string PrivateKey, string DataHash, string DataContent, ref List<Zephyr.Result> myResults)
        {
            int nReturnCode = -1;
            string strEncodedKey = "";

            try
            {
                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                myContent.Add("id", DataHash);
                myContent.Add("text", DataContent);
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                myContent.Add("encodedkey", strEncodedKey);

                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Perform upload up to certain number of failures
                int nTry = 1;
                // Construct web client object to make the request with params
                WebClient myClient = new WebClient();
                myClient.BaseAddress = ServiceURL;
                myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                // UploadValues performs implicit POST of NVC to BaseAddress + URI
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);

                XmlDocument addDocumentXml = new XmlDocument();
                while (nTry < 10)
                {
                    String responseXml = Encoding.ASCII.GetString(responseArray);
                    if (responseXml.StartsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))
                    {
                        addDocumentXml.LoadXml(Encoding.ASCII.GetString(responseArray));
                        GetSingleResultResponse getResultsResponse = new GetSingleResultResponse(addDocumentXml, DataHash, ref myResults);
                        nReturnCode = getResultsResponse.LastError;
#if DEBUG
                        System.Diagnostics.Trace.WriteLine(String.Format("Try {0}: nReturn {1}", nTry, nReturnCode));
#endif
                        if (nReturnCode != 0)
                        {
#if DEBUG
                            System.Diagnostics.Trace.WriteLine("ERROR: " + getResultsResponse.LastErrorString);
#endif
                            throw new Exception(getResultsResponse.LastErrorString);
                        }
                        break;
                    }
                    else
                    {
#if DEBUG
                        System.Diagnostics.Trace.WriteLine(String.Format("Try {0}: Return {1}", nTry, addDocumentXml.InnerText));
#endif
                        nTry++;
                        responseArray = myClient.UploadValues(strMethodURL, myContent);
                    }
                }

            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to add the document.", xmlErr);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("XML ERR: " + xmlErr.Message);
#endif
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to add the document.", err);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("ERROR: " + err.Message);
#endif
                throw wsErr;
            }

            return nReturnCode;
        }



        /// <summary>
        /// Wrapper around POST to web service method to add a feed to the project
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">Public key for web service request</param>
        /// <param name="PrivateKey">Private key for encoding web service request</param>
        /// <param name="ProjectKey">Project to add feed into</param>
        /// <param name="Identifier">Identifier for the feed</param>
        /// <param name="FeedUrl">Feed URL to add to project</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Status string</returns>
        public int WSAddFeed(string ServiceURL, string PublicKey, string PrivateKey, string ProjectKey, string Identifier, string FeedUrl)
        {
            int nReturnCode = -1;
            string strEncodedKey = "";

            try
            {
                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                myContent.Add("project", ProjectKey);
                myContent.Add("id", Identifier);
                myContent.Add("url", FeedUrl);
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                myContent.Add("encodedkey", strEncodedKey);

                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Construct web client object to make the request with params
                WebClient myClient = new WebClient();
                myClient.BaseAddress = ServiceURL;
                myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                // UploadValues performs implicit POST of NVC to BaseAddress + URI
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);

                XmlDocument addFeedXml = new XmlDocument();
                String responseXml = Encoding.ASCII.GetString(responseArray);
                if (responseXml.StartsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))
                {
                    addFeedXml.LoadXml(Encoding.ASCII.GetString(responseArray));
                    LXAResponse addFeedResponse = new LXAResponse(addFeedXml);
                    nReturnCode = addFeedResponse.LastError;
                }
            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to add the feed.", xmlErr);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("XML ERR: " + xmlErr.Message);
#endif
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to add the feed.", err);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("ERROR: " + err.Message);
#endif
                throw wsErr;
            }

            return nReturnCode;
        }

        /// <summary>
        /// Wrapper around POST to web service method to add a URL to the project
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">Public key for web service request</param>
        /// <param name="PrivateKey">Private key for encoding web service request</param>
        /// <param name="ProjectKey">Project to add URL into</param>
        /// <param name="Identifier">Identifier for the URL</param>
        /// <param name="Url">URL to add to project</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Status string</returns>
        public int WSAddURL(string ServiceURL, string PublicKey, string PrivateKey, string ProjectKey, string Identifier, string Url)
        {
            int nReturnCode = -1;
            string strEncodedKey = "";

            try
            {
                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                myContent.Add("project", ProjectKey);
                myContent.Add("id", Identifier);
                myContent.Add("url", Url);
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                myContent.Add("encodedkey", strEncodedKey);

                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Construct web client object to make the request with params
                WebClient myClient = new WebClient();
                myClient.BaseAddress = ServiceURL;
                myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                // UploadValues performs implicit POST of NVC to BaseAddress + URI
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);

                XmlDocument addUrlXml = new XmlDocument();
                String responseXml = Encoding.ASCII.GetString(responseArray);
                if (responseXml.StartsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))
                {
                    addUrlXml.LoadXml(Encoding.ASCII.GetString(responseArray));
                    LXAResponse addUrlResponse = new LXAResponse(addUrlXml);
                    nReturnCode = addUrlResponse.LastError;
                }
            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to add the URL.", xmlErr);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("XML ERR: " + xmlErr.Message);
#endif
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to add the URL.", err);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("ERROR: " + err.Message);
#endif
                throw wsErr;
            }

            return nReturnCode;
        }

        #endregion

        #region These functions are used to add data via queries into Tempest
        /// <summary>
        /// Wrapper around POST to web service method to add a query against Google News and Blogs to the project
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">Public key for web service request</param>
        /// <param name="PrivateKey">Private key for encoding web service request</param>
        /// <param name="ProjectKey">Project to add Google query into</param>
        /// <param name="Identifier">Identifier for the Google query</param>
        /// <param name="Query">The query that you want to execute, which will be executed against both Google News and Google Blogs.</param>
        /// <param name="Schedule">How often the feed should be polled in seconds. Defaults to 3600 (every hour). If you try to set it to less than an hour then it will default to an hour.</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Status string</returns>
        public int WSAddGoogleQuery(string ServiceURL, string PublicKey, string PrivateKey, string ProjectKey, string Identifier, string Query, int Schedule)
        {
            int nReturnCode = -1;
            string strEncodedKey = "";

            try
            {
                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                myContent.Add("project", ProjectKey);
                myContent.Add("id", Identifier);
                myContent.Add("query", Query);
                if (Schedule > 0)
                    myContent.Add("schedule", Schedule.ToString());
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                myContent.Add("encodedkey", strEncodedKey);

                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Construct web client object to make the request with params
                WebClient myClient = new WebClient();
                myClient.BaseAddress = ServiceURL;
                myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                // UploadValues performs implicit POST of NVC to BaseAddress + URI
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);

                XmlDocument addQueryXml = new XmlDocument();
                String responseXml = Encoding.ASCII.GetString(responseArray);
                if (responseXml.StartsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))
                {
                    addQueryXml.LoadXml(Encoding.ASCII.GetString(responseArray));
                    LXAResponse addQueryResponse = new LXAResponse(addQueryXml);
                    nReturnCode = addQueryResponse.LastError;
                }
            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to add the Google query.", xmlErr);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("XML ERR: " + xmlErr.Message);
#endif
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to add the Google query.", err);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("ERROR: " + err.Message);
#endif
                throw wsErr;
            }

            return nReturnCode;
        }

        /// <summary>
        /// Wrapper around POST to web service method to add a query against Twitter to the project
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">Public key for web service request</param>
        /// <param name="PrivateKey">Private key for encoding web service request</param>
        /// <param name="ProjectKey">Project to add Twitter query into</param>
        /// <param name="Identifier">Identifier for the Twitter query</param>
        /// <param name="Query">The query that you want to execute, which will be executed against the Twitter search API.</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Status string</returns>
        public int WSAddTwitterQuery(string ServiceURL, string PublicKey, string PrivateKey, string ProjectKey, string Identifier, string Query)
        {
            int nReturnCode = -1;
            string strEncodedKey = "";

            try
            {
                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                myContent.Add("project", ProjectKey);
                myContent.Add("id", Identifier);
                myContent.Add("query", Query);
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                myContent.Add("encodedkey", strEncodedKey);

                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Construct web client object to make the request with params
                WebClient myClient = new WebClient();
                myClient.BaseAddress = ServiceURL;
                myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                // UploadValues performs implicit POST of NVC to BaseAddress + URI
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);

                XmlDocument addQueryXml = new XmlDocument();
                String responseXml = Encoding.ASCII.GetString(responseArray);
                if (responseXml.StartsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))
                {
                    addQueryXml.LoadXml(Encoding.ASCII.GetString(responseArray));
                    LXAResponse addQueryResponse = new LXAResponse(addQueryXml);
                    nReturnCode = addQueryResponse.LastError;
                }
            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to add the Twitter query.", xmlErr);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("XML ERR: " + xmlErr.Message);
#endif
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to add the Twitter query.", err);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("ERROR: " + err.Message);
#endif
                throw wsErr;
            }

            return nReturnCode;
        }

        /// <summary>
        /// Wrapper around POST to web service method to add a query against Blogpulse to the project
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">Public key for web service request</param>
        /// <param name="PrivateKey">Private key for encoding web service request</param>
        /// <param name="ProjectKey">Project to add Blogpulse query into</param>
        /// <param name="Identifier">Identifier for the Blogpulse query</param>
        /// <param name="Query">The query that you want to execute, which will be executed against Blogpulse.</param>
        /// <param name="Schedule">How often the feed should be polled in seconds. Defaults to 3600 (every hour). If you try to set it to less than an hour then it will default to an hour.</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Status string</returns>
        public int WSAddBlogpulseQuery(string ServiceURL, string PublicKey, string PrivateKey, string ProjectKey, string Identifier, string Query, int Schedule)
        {
            int nReturnCode = -1;
            string strEncodedKey = "";

            try
            {
                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                myContent.Add("project", ProjectKey);
                myContent.Add("id", Identifier);
                myContent.Add("query", Query);
                if (Schedule > 0)
                    myContent.Add("schedule", Schedule.ToString());
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                myContent.Add("encodedkey", strEncodedKey);

                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Construct web client object to make the request with params
                WebClient myClient = new WebClient();
                myClient.BaseAddress = ServiceURL;
                myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                // UploadValues performs implicit POST of NVC to BaseAddress + URI
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);

                XmlDocument addQueryXml = new XmlDocument();
                String responseXml = Encoding.ASCII.GetString(responseArray);
                if (responseXml.StartsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))
                {
                    addQueryXml.LoadXml(Encoding.ASCII.GetString(responseArray));
                    LXAResponse addQueryResponse = new LXAResponse(addQueryXml);
                    nReturnCode = addQueryResponse.LastError;
                }
            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to add the Blogpulse query.", xmlErr);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("XML ERR: " + xmlErr.Message);
#endif
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to add the Blogpulse query.", err);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("ERROR: " + err.Message);
#endif
                throw wsErr;
            }

            return nReturnCode;
        }

        /// <summary>
        /// Wrapper around POST to web service method to add a query against Wordpress to the project
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">Public key for web service request</param>
        /// <param name="PrivateKey">Private key for encoding web service request</param>
        /// <param name="ProjectKey">Project to add Wordpress query into</param>
        /// <param name="Identifier">Identifier for the Wordpress query</param>
        /// <param name="Query">The query that you want to execute, which will be executed against Wordpress.</param>
        /// <param name="Schedule">How often the feed should be polled in seconds. Defaults to 3600 (every hour). If you try to set it to less than an hour then it will default to an hour.</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Status string</returns>
        public int WSAddWordpressQuery(string ServiceURL, string PublicKey, string PrivateKey, string ProjectKey, string Identifier, string Query, int Schedule)
        {
            int nReturnCode = -1;
            string strEncodedKey = "";

            try
            {
                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                myContent.Add("project", ProjectKey);
                myContent.Add("id", Identifier);
                myContent.Add("query", Query);
                if (Schedule > 0)
                    myContent.Add("schedule", Schedule.ToString());
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                myContent.Add("encodedkey", strEncodedKey);

                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Construct web client object to make the request with params
                WebClient myClient = new WebClient();
                myClient.BaseAddress = ServiceURL;
                myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                // UploadValues performs implicit POST of NVC to BaseAddress + URI
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);

                XmlDocument addQueryXml = new XmlDocument();
                String responseXml = Encoding.ASCII.GetString(responseArray);
                if (responseXml.StartsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))
                {
                    addQueryXml.LoadXml(Encoding.ASCII.GetString(responseArray));
                    LXAResponse addQueryResponse = new LXAResponse(addQueryXml);
                    nReturnCode = addQueryResponse.LastError;
                }
            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to add the Wordpress query.", xmlErr);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("XML ERR: " + xmlErr.Message);
#endif
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to add the Wordpress query.", err);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("ERROR: " + err.Message);
#endif
                throw wsErr;
            }

            return nReturnCode;
        }

        #endregion

        #region This function is used to add data contained within a Tempest loadfile
        /// <summary>
        /// Wrapper around POST to web service method to add a loadfile document to the project
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">User public key</param>
        /// <param name="PrivateKey">Private key for encoding request</param>
        /// <param name="ProjectKey">Project to add content to</param>
        /// <param name="Loadfile">The loadfile</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Status string</returns>
        public string WSAddLoadfile(string ServiceURL, string PublicKey, string PrivateKey, string ProjectKey, string Loadfile)
        {
            int nReturnCode = -1;
            string strStatus = "";
            string strEncodedKey = "";

            try
            {
                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Perform upload up to certain number of failures
                int nTry = 1;

                // Construct web client object to make the request with params
                // Read loadfile contents into byte array
                byte[] data = new byte[Loadfile.Length];
                data = ASCIIEncoding.UTF8.GetBytes(Loadfile);

                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                myContent.Add("project", ProjectKey);
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);

                // Generate post objects, needs to be in a dictionary to support object
                Dictionary<string, object> postParameters = new Dictionary<string, object>();
                postParameters.Add("publickey", PublicKey);
                postParameters.Add("project", ProjectKey);
                postParameters.Add("encodedkey", strEncodedKey);
                postParameters.Add("loadfile", new FormUpload.FileParameter(data, strEncodedKey + ".xml", "text/xml"));

                // Create request and receive response
                string postURL = ServiceURL + GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                string userAgent = "Lexascope Excel";
                HttpWebResponse webResponse = FormUpload.MultipartFormDataPost(postURL, userAgent, postParameters);

                // Process response
                StreamReader responseReader = new StreamReader(webResponse.GetResponseStream());
                String responseXml = responseReader.ReadToEnd();
                webResponse.Close();

                XmlDocument addDocumentXml = new XmlDocument();
                while (nTry < 10)
                {
                    if (responseXml.StartsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))
                    {
                        addDocumentXml.LoadXml(responseXml);
                        LXAResponse addDocumentResponse = new LXAResponse(addDocumentXml);
                        nReturnCode = addDocumentResponse.LastError;
#if DEBUG
                        System.Diagnostics.Trace.WriteLine(String.Format("Try {0}: nReturn {1}", nTry, nReturnCode));
#endif
                        if (nReturnCode == 0)
                        {
                            strStatus = "ACTIVE";
                        }
                        else
                        {
                            strStatus = "ERROR";
#if DEBUG
                            System.Diagnostics.Trace.WriteLine("ERROR: " + addDocumentResponse.LastErrorString);
#endif
                        }
                        break;
                    }
                    else
                    {
#if DEBUG
                        System.Diagnostics.Trace.WriteLine(String.Format("Try {0}: Return {1}", nTry, addDocumentXml.InnerText));
#endif
                        nTry++;
                    }
                }

            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to add the document.", xmlErr);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("XML ERR: " + xmlErr.Message);
#endif
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to add the document.", err);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("ERROR: " + err.Message);
#endif
                throw wsErr;
            }

            return strStatus;
        }
        
        #endregion

        #region These functions are used to add additional pieces of metadata to assets as they are processed via Tempest
        /// <summary>
        /// Wrapper around call to web service method to add metadata to a document
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">Public key for web service request</param>
        /// <param name="PrivateKey">Private key for encoding web service request</param>
        /// <param name="ProjectKey">Project containing asset which metadata will be added to</param>
        /// <param name="DataHash">Identifier for the content</param>
        /// <param name="DataLabel">Label for the content</param>
        /// <param name="DataContent">Value for metadata</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Integer status code</returns>
        public int WSAddMetadata(string ServiceURL, string PublicKey, string PrivateKey, string ProjectKey, string DataHash, string DataLabel, string DataContent)
        {
            int nReturnCode = -1;
            string strEncodedKey = "";

            try
            {
                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                myContent.Add("project", ProjectKey);
                myContent.Add("id", DataHash);
                myContent.Add("label", DataLabel);
                myContent.Add("value", DataContent);
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                myContent.Add("encodedkey", strEncodedKey);

#if DEBUG
                foreach (string strname in myContent.Keys)
                    System.Diagnostics.Trace.WriteLine(String.Format("{0}: {1}", strname, myContent[strname].ToString()));
#endif
                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Construct web client object to make the request with params
                WebClient myClient = new WebClient();
                myClient.BaseAddress = ServiceURL;
                myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");

                // Perform upload up to certain number of failures
                int nTry = 1;
                // UploadValues performs implicit POST of NVC to BaseAddress + URI
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);
                XmlDocument addMetadataXml = new XmlDocument();
                while (nTry < 10)
                {
                    String responseXml = Encoding.ASCII.GetString(responseArray);
                    if (responseXml.StartsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))
                    {
                        addMetadataXml.LoadXml(Encoding.ASCII.GetString(responseArray));
                        LXAResponse addMetadataResponse = new LXAResponse(addMetadataXml);
                        nReturnCode = addMetadataResponse.LastError;
#if DEBUG
                        System.Diagnostics.Trace.WriteLine(String.Format("Try {0}: nReturn {1}", nTry, nReturnCode));
#endif
                        break;
                    }
                    else
                    {
#if DEBUG
                        System.Diagnostics.Trace.WriteLine(String.Format("Try {0}: Return {1}", nTry, addMetadataXml.InnerText));
#endif
                        nTry++;
                        responseArray = myClient.UploadValues(strMethodURL, myContent);
                    }
                }
            }
            catch (XmlException xmlErr)
            {
#if DEBUG
                System.Diagnostics.Trace.WriteLine(String.Format("XML ERR: {0}", xmlErr.Message));
#endif
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to add metadata.", xmlErr);
                throw wsXmlErr;
            }
            catch (Exception err)
            {
#if DEBUG
                System.Diagnostics.Trace.WriteLine(String.Format("ERR: {0}", err.Message));
#endif
                Exception wsErr = new Exception("An error occurred in the web service call to add metadata.", err);
                throw wsErr;
            }
            return nReturnCode;
        }

        /// <summary>
        /// Wrapper around POST to web service method to add metadata to a feed in the project
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">Public key for web service request</param>
        /// <param name="PrivateKey">Private key for encoding web service request</param>
        /// <param name="ProjectKey">Project to add feed into</param>
        /// <param name="Identifier">Identifier for the feed</param>
        /// <param name="Label">Metadata label</param>
        /// <param name="Value">Metadata value</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Status string</returns>
        public int WSAddFeedMetadata(string ServiceURL, string PublicKey, string PrivateKey, string ProjectKey, string Identifier, string Label, string Value)
        {
            int nReturnCode = -1;
            string strEncodedKey = "";

            try
            {
                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                myContent.Add("project", ProjectKey);
                myContent.Add("id", Identifier);
                myContent.Add("label", Label);
                myContent.Add("value", Value);
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                myContent.Add("encodedkey", strEncodedKey);

                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Construct web client object to make the request with params
                WebClient myClient = new WebClient();
                myClient.BaseAddress = ServiceURL;
                myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                // UploadValues performs implicit POST of NVC to BaseAddress + URI
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);

                XmlDocument addFeedMetadataXml = new XmlDocument();
                String responseXml = Encoding.ASCII.GetString(responseArray);
                if (responseXml.StartsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))
                {
                    addFeedMetadataXml.LoadXml(Encoding.ASCII.GetString(responseArray));
                    LXAResponse addFeedMetadataResponse = new LXAResponse(addFeedMetadataXml);
                    nReturnCode = addFeedMetadataResponse.LastError;
                }
            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to add feed metadata.", xmlErr);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("XML ERR: " + xmlErr.Message);
#endif
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to add feed metadata.", err);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("ERROR: " + err.Message);
#endif
                throw wsErr;
            }

            return nReturnCode;
        }

        #endregion

        #region These functions are used to modify the processing of the data as it flows through Tempest
        /// <summary>
        /// Wrapper around POST to web service method to add a tag list to a project
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">Public key for web service request</param>
        /// <param name="PrivateKey">Private key for encoding web service request</param>
        /// <param name="ProjectKey">Project to add tag list to</param>
        /// <param name="Taglist">The tag list itself</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Status string</returns>
        public int WSAddTagList(string ServiceURL, string PublicKey, string PrivateKey, string ProjectKey, List<Zephyr.Tag> Taglist)
        {
            int nReturnCode = -1;
            string strEncodedKey = "";

            try
            {
                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                myContent.Add("project", ProjectKey);
                StringBuilder sbList = new StringBuilder();
                foreach (Zephyr.Tag aTag in Taglist)
                    sbList.AppendFormat("{0}\t{1}\n", aTag.Label, aTag.Query);
                myContent.Add("list", sbList.ToString());
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                myContent.Add("encodedkey", strEncodedKey);

                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Construct web client object to make the request with params
                WebClient myClient = new WebClient();
                myClient.BaseAddress = ServiceURL;
                myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                // UploadValues performs implicit POST of NVC to BaseAddress + URI
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);

                XmlDocument addListXml = new XmlDocument();
                String responseXml = Encoding.ASCII.GetString(responseArray);
                if (responseXml.StartsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))
                {
                    addListXml.LoadXml(Encoding.ASCII.GetString(responseArray));
                    LXAResponse addListResponse = new LXAResponse(addListXml);
                    nReturnCode = addListResponse.LastError;
                }
            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to add the list of tags.", xmlErr);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("XML ERR: " + xmlErr.Message);
#endif
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to add the list of tags.", err);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("ERROR: " + err.Message);
#endif
                throw wsErr;
            }

            return nReturnCode;
        }

        /// <summary>
        /// Wrapper around POST to web service method to add a list of custom entities to a project
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">Public key for web service request</param>
        /// <param name="PrivateKey">Private key for encoding web service request</param>
        /// <param name="ProjectKey">Project to add list of custom entities to</param>
        /// <param name="Taglist">The entity list itself</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Status string</returns>
        public int WSAddCustomEntityList(string ServiceURL, string PublicKey, string PrivateKey, string ProjectKey, List<Zephyr.CustomEntity> Entitylist)
        {
            int nReturnCode = -1;
            string strEncodedKey = "";

            try
            {
                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                myContent.Add("project", ProjectKey);
                StringBuilder sbList = new StringBuilder();
                foreach (Zephyr.CustomEntity aEntity in Entitylist)
                    sbList.AppendFormat("{0}\t{1}\t{2}\n", aEntity.Form, aEntity.Normalized, aEntity.Label);
                myContent.Add("list", sbList.ToString());
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                myContent.Add("encodedkey", strEncodedKey);

                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Construct web client object to make the request with params
                WebClient myClient = new WebClient();
                myClient.BaseAddress = ServiceURL;
                myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                // UploadValues performs implicit POST of NVC to BaseAddress + URI
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);

                XmlDocument addListXml = new XmlDocument();
                String responseXml = Encoding.ASCII.GetString(responseArray);
                if (responseXml.StartsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))
                {
                    addListXml.LoadXml(Encoding.ASCII.GetString(responseArray));
                    LXAResponse addListResponse = new LXAResponse(addListXml);
                    nReturnCode = addListResponse.LastError;
                }
            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to add the list of custom entities.", xmlErr);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("XML ERR: " + xmlErr.Message);
#endif
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to add the list of custom entities.", err);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("ERROR: " + err.Message);
#endif
                throw wsErr;
            }

            return nReturnCode;
        }

        #endregion

        #region These functions are used to retrieve state information from Tempest
        /// <summary>
        /// Wrapper around call to web service method to get list of projects for provided public key
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">Public key to retrieve projects for</param>
        /// <param name="PrivateKey">Private key to authenticate the request</param>
        /// <param name="ProjectNames">Return string list of project names</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Integer return code</returns>
        public int WSGetProjectList(string ServiceURL, string PublicKey, string PrivateKey, ref List<string> ProjectNames)
        {
            int nStatus = 0;
            string strEncodedKey = "";

            try
            {
                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                myContent.Add("encodedkey", strEncodedKey);

                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Construct web client object to make the request with params
                WebClient myClient = new WebClient();
                myClient.BaseAddress = ServiceURL;
                myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                // UploadValues performs implicit POST of NVC to BaseAddress + URI
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);

                XmlDocument getProjectListXml = new XmlDocument();
                string strResponse = Encoding.ASCII.GetString(responseArray);
                getProjectListXml.LoadXml(strResponse);
                GetProjectListResponse getProjectListResponse = new GetProjectListResponse(getProjectListXml);
                nStatus = getProjectListResponse.LastError;
                if (nStatus == 0)
                    ProjectNames = getProjectListResponse.ProjectNames;
            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to get the project list.", xmlErr);
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to get project list.", err);
                throw wsErr;
            }

            return nStatus;
        }

        #endregion

        #region These functions are used to get processed data out of Tempest
        /// <summary>
        /// Wrapper around call to web service method to get all results
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">User public key</param>
        /// <param name="PrivateKey">Private key for encoding request</param>
        /// <param name="ProjectKey">Project to add content to</param>
        /// <param name="myResults">Return object list of results for all assets</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Integer return status</returns>
        public int WSAllResults(string ServiceURL, string PublicKey, string PrivateKey, string ProjectKey, ref List<Zephyr.Result> myResults)
        {
            int nStatus = 0;
            string strEncodedKey = "";
            string strLastDate = "";
            bool bAllResults = false;
            try
            {
                while (!bAllResults)
                {
                    // Construct encoded key for request
                    NameValueCollection myContent = new NameValueCollection();
                    myContent.Add("publickey", PublicKey);
                    myContent.Add("project", ProjectKey);
                    if (strLastDate.Length > 0)
                    {
                        myContent.Add("date", strLastDate.Substring(0, 8));
                        myContent.Add("time", strLastDate.Substring(8, 6));
                    }
                    strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                    myContent.Add("encodedkey", strEncodedKey);

                    // Set static property in case of slow connections?
                    System.Net.ServicePointManager.Expect100Continue = false;

                    // Construct web client object to make the request with params
                    WebClient myClient = new WebClient();
                    myClient.BaseAddress = ServiceURL;
                    myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                    // UploadValues performs implicit POST of NVC to BaseAddress + URI
                    string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                    byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);

                    XmlDocument getResultsXml = new XmlDocument();
                    string strResults = Encoding.ASCII.GetString(responseArray);
                    getResultsXml.LoadXml(strResults);
                    GetResultsResponse getResultsResponse = new GetResultsResponse(getResultsXml, ref myResults);

                    // Determine if there are more results to retrieve
                    if (getResultsResponse.LastDate.Length == 0)
                        bAllResults = true;
                    else
                        strLastDate = getResultsResponse.LastDate;
                }
            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to get companies.", xmlErr);
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to get companies.", err);
                throw wsErr;
            }

            return nStatus;
        }

        /// <summary>
        /// Wrapper around call to web service method to get count of all results
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">User public key</param>
        /// <param name="PrivateKey">Private key for encoding request</param>
        /// <param name="ProjectKey">Project to add content to</param>
        /// <param name="ResultCount">Return integer number of results to expect</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Integer return status</returns>
        public int WSResultCount(string ServiceURL, string PublicKey, string PrivateKey, string ProjectKey, ref int ResultCount)
        {
            int nStatus = 0;
            string strEncodedKey = "";

            try
            {
                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                myContent.Add("project", ProjectKey);
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                myContent.Add("encodedkey", strEncodedKey);

                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Construct web client object to make the request with params
                WebClient myClient = new WebClient();
                myClient.BaseAddress = ServiceURL;
                myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                // UploadValues performs implicit POST of NVC to BaseAddress + URI
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);

                XmlDocument getResultsXml = new XmlDocument();
                string strResults = Encoding.ASCII.GetString(responseArray);
                getResultsXml.LoadXml(strResults);
                GetResultCountResponse getResultsResponse = new GetResultCountResponse(getResultsXml, ref ResultCount);
            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to get companies.", xmlErr);
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to get companies.", err);
                throw wsErr;
            }

            return nStatus;
        }

        #endregion

        #region These functions are used to remove data and feeds from Tempest
        /// <summary>
        /// Wrapper around call to web service method to remove a document from the project
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">User public key</param>
        /// <param name="PrivateKey">Private key for encoding request</param>
        /// <param name="ProjectKey">Project containing the content</param>
        /// <param name="DataHash">Identfier for the content</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Integer status code</returns>
        public int WSDeleteDocument(string ServiceURL, string PublicKey, string PrivateKey, string ProjectKey, string DataHash)
        {
            int nReturnCode = 0;
            string strEncodedKey = "";

            try
            {
                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                myContent.Add("project", ProjectKey);
                myContent.Add("id", DataHash);
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                myContent.Add("encodedkey", strEncodedKey);

                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Construct web client object to make the request with params
                WebClient myClient = new WebClient();
                myClient.BaseAddress = ServiceURL;
                myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                // UploadValues performs implicit POST of NVC to BaseAddress + URI
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);

                XmlDocument deleteAssetXml = new XmlDocument();
                deleteAssetXml.LoadXml(Encoding.ASCII.GetString(responseArray));
                LXAResponse deleteAssetResponse = new LXAResponse(deleteAssetXml);
                nReturnCode = deleteAssetResponse.LastError;
            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to delete the document.", xmlErr);
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to delete the document.", err);
                throw wsErr;
            }

            return nReturnCode;
        }

        /// <summary>
        /// Wrapper around call to web service method to remove a feed from the project
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">User public key</param>
        /// <param name="PrivateKey">Private key for encoding request</param>
        /// <param name="ProjectKey">Project containing the feed</param>
        /// <param name="DataHash">Identfier for the feed</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Integer status code</returns>
        public int WSDeleteFeed(string ServiceURL, string PublicKey, string PrivateKey, string ProjectKey, string Identifier)
        {
            int nReturnCode = 0;
            string strEncodedKey = "";

            try
            {
                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                myContent.Add("project", ProjectKey);
                myContent.Add("feed_id", Identifier);
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                myContent.Add("encodedkey", strEncodedKey);

                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Construct web client object to make the request with params
                WebClient myClient = new WebClient();
                myClient.BaseAddress = ServiceURL;
                myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                // UploadValues performs implicit POST of NVC to BaseAddress + URI
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);

                XmlDocument deleteFeedXml = new XmlDocument();
                deleteFeedXml.LoadXml(Encoding.ASCII.GetString(responseArray));
                LXAResponse deleteFeedResponse = new LXAResponse(deleteFeedXml);
                nReturnCode = deleteFeedResponse.LastError;
            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to delete the feed.", xmlErr);
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to delete the feed.", err);
                throw wsErr;
            }

            return nReturnCode;
        }

        /// <summary>
        /// Wrapper around call to web service method to delete a project
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">User public key</param>
        /// <param name="PrivateKey">Private key for encoding request</param>
        /// <param name="ProjectKey">Project to delete</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Integer status code</returns>
        public int WSDeleteProject(string ServiceURL, string PublicKey, string PrivateKey, string ProjectKey)
        {
            int nReturnCode = 0;
            string strEncodedKey = "";

            try
            {
                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                myContent.Add("project", ProjectKey);
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                myContent.Add("encodedkey", strEncodedKey);

                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Construct web client object to make the request with params
                WebClient myClient = new WebClient();
                myClient.BaseAddress = ServiceURL;
                myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                // UploadValues performs implicit POST of NVC to BaseAddress + URI
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);

                XmlDocument deleteProjectXml = new XmlDocument();
                deleteProjectXml.LoadXml(Encoding.ASCII.GetString(responseArray));
                LXAResponse deleteProjectResponse = new LXAResponse(deleteProjectXml);
                nReturnCode = deleteProjectResponse.LastError;
            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to delete the project.", xmlErr);
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to delete the project.", err);
                throw wsErr;
            }

            return nReturnCode;
        }

        #endregion

        #region These functions are used to alter the limits on your account
        /// <summary>
        /// Wrapper around POST to web service method to set content quota limits
        /// </summary>
        /// <param name="ServiceURL">URL for web service</param>
        /// <param name="PublicKey">User public key</param>
        /// <param name="PrivateKey">Private key for encoding request</param>
        /// <param name="UserLimit">The maximum number of user assets you want to be able to add a day.</param>
        /// <param name="FeedLimit">The maximum number of feed assets you want added in a day.</param>
        /// <exception cref="System.XmlException">Thrown if malformed XML is received back from web service call</exception>
        /// <exception cref="System.Exception">Thrown if exception caught in web service call</exception>
        /// <returns>Status string</returns>
        public int WSSetLimit(string ServiceURL, string PublicKey, string PrivateKey, int UserLimit, int FeedLimit)
        {
            int nReturnCode = -1;
            string strEncodedKey = "";

            try
            {
                // Construct encoded key for request
                NameValueCollection myContent = new NameValueCollection();
                myContent.Add("publickey", PublicKey);
                myContent.Add("userlimit", UserLimit.ToString());
                myContent.Add("feedlimit", FeedLimit.ToString());
                strEncodedKey = GenerateRequestEncodedKey(PublicKey, PrivateKey, myContent);
                myContent.Add("encodedkey", strEncodedKey);

                // Set static property in case of slow connections?
                System.Net.ServicePointManager.Expect100Continue = false;

                // Construct web client object to make the request with params
                WebClient myClient = new WebClient();
                myClient.BaseAddress = ServiceURL;
                myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                // UploadValues performs implicit POST of NVC to BaseAddress + URI
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);

                XmlDocument setLimitXml = new XmlDocument();
                String responseXml = Encoding.ASCII.GetString(responseArray);
                if (responseXml.StartsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))
                {
                    setLimitXml.LoadXml(Encoding.ASCII.GetString(responseArray));
                    LXAResponse setLimitResponse = new LXAResponse(setLimitXml);
                    nReturnCode = setLimitResponse.LastError;
                }
            }
            catch (XmlException xmlErr)
            {
                Exception wsXmlErr = new Exception("An error occurred in the response from the web service call to set content limits.", xmlErr);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("XML ERR: " + xmlErr.Message);
#endif
                throw wsXmlErr;
            }
            catch (Exception err)
            {
                Exception wsErr = new Exception("An error occurred in the web service call to set content limits.", err);
#if DEBUG
                System.Diagnostics.Trace.WriteLine("ERROR: " + err.Message);
#endif
                throw wsErr;
            }

            return nReturnCode;
        }

        #endregion

        #region Miscellaneous functions
        /// <summary>
        /// Ping web service to check that URL can be reached and responds with a web service response
        /// </summary>
        /// <param name="ServiceURL">The root URL for the web service</param>
        /// <exception cref="System.Exception">Thrown if error occurs in retrieving web service URL or requesting</exception>
        /// <returns>True/False indication that the web API responds at the URL provided</returns>
        public bool WSServiceCheck(string ServiceURL)
        {
            bool bRet = false;

            // Check that a specified page can be accessed at provided URL
            WebClient myClient = new WebClient();
            myClient.BaseAddress = ServiceURL;
            myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
            NameValueCollection myContent = new NameValueCollection();

            // Set static property in case of slow connections?
            System.Net.ServicePointManager.Expect100Continue = false;

            // UploadValues performs implicit POST of NVC to BaseAddress + URI
            try
            {
                // Retrieve web service method URL from resource file.
                string strMethodURL = GetMethodURL(System.Reflection.MethodInfo.GetCurrentMethod().Name);
                byte[] responseArray = myClient.UploadValues(strMethodURL, myContent);
                string sResponse = Encoding.ASCII.GetString(responseArray);

                if (!String.IsNullOrEmpty(sResponse))
                    bRet = true;
            }
            catch (Exception err)
            {
                Exception checkErr = new Exception("Error occurred while checking for Lexascope service status. Check Service URL provided.", err);
                throw checkErr;
            }

            return bRet;
        }
        #endregion

        #endregion // Lexascope web service interface

        #region Private utility functions

        /// <summary>
        /// Returns a fully encoded string for use in API calls
        /// </summary>
        /// <param name="InputString">Unencoded string</param>
        /// <returns>String that is HTML encoded with additional characters encoded.</returns>
        private string FullEncode(string InputString)
        {
            string strReturn = InputString;
            strReturn = HttpUtility.UrlEncode(strReturn);
            strReturn = strReturn.Replace("+", "%20");
            strReturn = strReturn.Replace(" ", "%20");
            // Periods don't need to be encoded
            //strReturn = strReturn.Replace(".","%2E");
            strReturn = strReturn.Replace("/", "%2F");
            strReturn = strReturn.Replace("'", "%27");
            strReturn = strReturn.Replace("(", "%28");
            strReturn = strReturn.Replace(")", "%29");
            strReturn = strReturn.Replace("*", "%2A");
            strReturn = strReturn.Replace("!", "%21");
            strReturn = strReturn.Replace("@", "%40");
            strReturn = strReturn.Replace("\\", "%5C");

            // IMPORTANT: the .Net UrlEncode function above encodes with lower-casing. The PHP functions
            // expect upper case encoding when decoding. Thus, "." is encoded as "%2c", which does not 
            // equal the expected "%2C" for decoding. Need to iterate through the string and uppercase
            // selected characters which represent encoded characters to ensure proper decoding.
            char[] chars = strReturn.ToCharArray();
            StringBuilder encodedValue = new StringBuilder();
            for (int i=0; i<chars.Length; i++)
            {
                if (chars[i].CompareTo('%') == 0)
                {
                    encodedValue.Append(String.Format("{0}{1}{2}", chars[i], chars[i + 1].ToString().ToUpper(), chars[i + 2].ToString().ToUpper()));
                    i = i + 2;
                }
                else
                {
                    encodedValue.Append(chars[i]);
                }
            }
            return encodedValue.ToString();
        }

        /// <summary>
        /// Generates the encoded key for a request based on parameters and user credentials.
        /// </summary>
        /// <param name="PublicKey">Public key for the requesting user</param>
        /// <param name="PrivateKey">Private key for the requesting user</param>
        /// <param name="Params">NVC containing request parameters to be sent</param>
        /// <returns>Encoded key for use in web service request</returns>
        private string GenerateRequestEncodedKey(string PublicKey, string PrivateKey, NameValueCollection Params)
        {
            StringBuilder sbUnencodedKey = new StringBuilder();
            string strEncodedKey = "";

            // Build the string to be encoded as MD5 hash
            sbUnencodedKey.Append(PublicKey);
            sbUnencodedKey.Append(PrivateKey);
            foreach (string Name in Params.Keys)
            {
#if DEBUG
                System.Diagnostics.Trace.WriteLine(String.Format("{0}={1}", Name, FullEncode(Params[Name])));
#endif
                sbUnencodedKey.AppendFormat("{0}={1}", Name, FullEncode(Params[Name]));
            }

            byte[] textBytes = System.Text.Encoding.Default.GetBytes(sbUnencodedKey.ToString());
            try
            {
                System.Security.Cryptography.MD5CryptoServiceProvider cryptHandler;
                cryptHandler = new System.Security.Cryptography.MD5CryptoServiceProvider();
                byte[] hash = cryptHandler.ComputeHash(textBytes);
                string outputStr = "";
                foreach (byte a in hash)
                {
                    if (a < 16)
                        outputStr += "0" + a.ToString("x");
                    else
                        outputStr += a.ToString("x");
                }
                strEncodedKey = outputStr;
            }
            catch
            {
                throw;
            }

            return strEncodedKey;
        }

        /// <summary>
        /// Retrieves the web service method URL from string resources for internal method.
        /// </summary>
        /// <param name="MethodName">Name of internal method</param>
        /// <returns>Appropriate web service URL for API method</returns>
        private string GetMethodURL(string MethodName)
        {
            string strUrl = "";
            try
            {
                ResourceManager rm = LexaScopeNET.Properties.Resources.ResourceManager;
                // Retrieve string value for method
                strUrl = rm.GetString(MethodName);
            }
            catch (Exception err)
            {
                throw err;
            }
            return strUrl;
        }
        #endregion // Private utility functions

    }

    /// <summary>
    /// Helper class for file upload via POST with additional params
    /// </summary>
    // Based on technique described at: http://www.briangrinstead.com/blog/multipart-form-post-in-c
    public static class FormUpload
    {
        private static readonly Encoding encoding = Encoding.UTF8;
        public static HttpWebResponse MultipartFormDataPost(string postUrl, string userAgent, Dictionary<string, object> postParameters)
        {
            DateTime requestTimestamp = DateTime.Now;
            string formDataBoundary = "-----------------------------" + requestTimestamp.Ticks.ToString("x");
            string contentType = "multipart/form-data; boundary=" + formDataBoundary;

            byte[] formData = GetMultipartFormData(postParameters, formDataBoundary);

            return PostForm(postUrl, userAgent, contentType, formData);
        }
        private static HttpWebResponse PostForm(string postUrl, string userAgent, string contentType, byte[] formData)
        {
            HttpWebRequest request = WebRequest.Create(postUrl) as HttpWebRequest;

            if (request == null)
            {
                throw new NullReferenceException("request is not a http request");
            }

            // Set up the request properties
            request.Method = "POST";
            request.ContentType = contentType;
            request.UserAgent = userAgent;
            request.CookieContainer = new CookieContainer();
            request.ContentLength = formData.Length;  // We need to count how many bytes we're sending. 

            using (Stream requestStream = request.GetRequestStream())
            {
                // Push it out there
                requestStream.Write(formData, 0, formData.Length);
                requestStream.Close();
            }

            return request.GetResponse() as HttpWebResponse;
        }

        private static byte[] GetMultipartFormData(Dictionary<string, object> postParameters, string boundary)
        {
            Stream formDataStream = new System.IO.MemoryStream();

            foreach (var param in postParameters)
            {
                if (param.Value is FileParameter)
                {
                    FileParameter fileToUpload = (FileParameter)param.Value;

                    // Add just the first part of this param, since we will write the file data directly to the Stream
                    string header = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\";\r\nContent-Type: {3}\r\n\r\n",
                        boundary,
                        param.Key,
                        fileToUpload.FileName ?? param.Key,
                        fileToUpload.ContentType ?? "application/octet-stream");

                    formDataStream.Write(encoding.GetBytes(header), 0, header.Length);

                    // Write the file data directly to the Stream, rather than serializing it to a string.
                    formDataStream.Write(fileToUpload.File, 0, fileToUpload.File.Length);
                }
                else
                {
                    string postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}\r\n",
                        boundary,
                        param.Key,
                        param.Value);
                    formDataStream.Write(encoding.GetBytes(postData), 0, postData.Length);
                }
            }

            // Add the end of the request
            string footer = "\r\n--" + boundary + "--\r\n";
            formDataStream.Write(encoding.GetBytes(footer), 0, footer.Length);

            // Dump the Stream into a byte[]
            formDataStream.Position = 0;
            byte[] formData = new byte[formDataStream.Length];
            formDataStream.Read(formData, 0, formData.Length);
            formDataStream.Close();

            return formData;
        }

        public class FileParameter
        {
            public byte[] File { get; set; }
            public string FileName { get; set; }
            public string ContentType { get; set; }
            public FileParameter(byte[] file) : this(file, null) { }
            public FileParameter(byte[] file, string filename) : this(file, filename, null) { }
            public FileParameter(byte[] file, string filename, string contenttype)
            {
                File = file;
                FileName = filename;
                ContentType = contenttype;
            }
        }
    }
}
